package treeedit;

import java.io.*;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

public class CodeModel implements TreeModel 
{
	protected List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
	protected CodeNode root;
	
	public void addTreeModelListener(TreeModelListener l) {
//		System.out.println("Adding listener");
		if(!listeners.contains(l))listeners.add(l);	
	}

	public Object getChild (Object parent, int index) 
	{
		CodeNode p = (CodeNode) parent;
		if(p.getChildren().size() < index )
			return null;
		
		return p.getChildren().get(index);	
	}

	public int getChildCount(Object parent) 
	{
		CodeNode p = (CodeNode) parent;
		return p.getChildren().size();
	}

	public int getIndexOfChild(Object parent, Object child) {
		CodeNode p = (CodeNode) parent;
		CodeNode c = (CodeNode) child;
		return p.getChildren().indexOf(child);
	}

	public Object getRoot() {
		return root;
	}

	public boolean isLeaf(Object node) 
	{
		return ((CodeNode) node).getChildren().size() == 0 ;
	}

	public void removeTreeModelListener(TreeModelListener l) {
		listeners.remove(l);
	}

	public void valueForPathChanged(TreePath path, Object newValue) {
		for(TreeModelListener l : listeners)
		{
			l.treeStructureChanged(new TreeModelEvent(this,new TreePath(root)));
		}
	}
	
	public TreePath addNewBrotherNode(JTree tree, TreePath path)
	{
		CodeNode c = ((CodeNode) path.getLastPathComponent()).getParent();
		CodeNode n;
		if(c == null) return null;
		
		c.insertAfter((CodeNode)path.getLastPathComponent(),n = new CodeNode(""));
		valueForPathChanged(new TreePath(root),"");
		return path.getParentPath().pathByAddingChild(n);
	}

	public TreePath addNewBrotherNodeUp(JTree tree, TreePath path)
	{
		CodeNode c = ((CodeNode) path.getLastPathComponent()).getParent();
		CodeNode n;
		if(c == null) return null;
		
		c.insertBefore((CodeNode)path.getLastPathComponent(),n = new CodeNode(""));
		valueForPathChanged(new TreePath(root),"");
		return path.getParentPath().pathByAddingChild(n);
	}

	
	public TreePath addNewClosingNode ( JTree tree, TreePath path)
	{
		CodeNode n,c = ((CodeNode) path.getLastPathComponent()).getParent();
		if(c == null) return null;
		CodeNode p = c.getParent();
		if(p == null) return null;
		
		p.insertAfter(c,n = new CodeNode(""));
		valueForPathChanged(new TreePath(root),"");
		return path.getParentPath().getParentPath().pathByAddingChild(n);
	}
	
	//Return the treepath of the added node
	public TreePath addNewChildNode(JTree tree, TreePath path)
	{
		CodeNode c = ((CodeNode) path.getLastPathComponent());
		CodeNode n;
		
		c.insertAtIndex(n = new CodeNode(""),0);
		
		valueForPathChanged(new TreePath(root),"");
		
		return path.pathByAddingChild(n);

	}
	
	public void moveInto(JTree tree, CodeNode what, CodeNode where )
	{
		//Not moving root
		if(what.getParent() == null || where.equals(what)) return;
		if(what.isAncestorOf(where)) return;
		
		if(where.getChildren().size() == 0 && !where.getCode().equals("( )") )
		{
			return ;
		}
		
		what.getParent().remove(what);
		if(where.isFunction()) where.insertAtIndex(what, 1);
		else where.insertAtIndex(what, 0);
		
		
		
		return;
	
	}
	
	public void moveBefore(JTree tree, CodeNode what, CodeNode where)
	{
		if(what.equals(where) ) return ;
		if(what.isAncestorOf(where)) return ;
		if(what.getParent() != null)
			what.getParent().remove(what);
		where.getParent().insertBefore(where, what);
		
		
	}
	
	public void moveAfter(JTree tree, CodeNode what, CodeNode where)
	{
		if(what.getParent() == null || what.equals(where) ) return ;
		if(what.isAncestorOf(where)) return ;
		what.getParent().remove(what);
		where.getParent().insertAfter(where, what);
		
		
	}
	
	
	public void deleteNode(JTree tree,TreePath path)
	{
		CodeNode node = (CodeNode)path.getLastPathComponent();
		CodeNode parent = node.getParent();
		parent.remove(node);
		valueForPathChanged(new TreePath(root),"");
	}
	
	public void createMock()
	{
		root = new CodeNode("Root");
	}

	public void load(String path) throws IOException, ParseException{
		CodeParser parser = new CodeParser();
		root = parser.parse(new FileReader(path));
		valueForPathChanged(new TreePath(root),"");
		

	}

	public void save(String path) throws IOException
	{
		
		FileWriter fw = new FileWriter(path);
		if(root != null && root.getChildren().size() != 0)
			fw.write(root.getFileContent());
		fw.close();
		
	}
	
	
}
